﻿/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Bio.Data.Interfaces;
using Bio.Views.Structure.Editor.Internal.Editing;
using Bio.Views.Structure.Editor.Internal.Rendering.Interfaces;

namespace Bio.Views.Structure.Editor.Internal.Rendering.Visuals
{
    public class RangeStructureElementVisualComponent : DependencyObject, IStructureElementVisualComponent
    {
        #region Events

        public event EventHandler<MixedRangeEditSetEventArgs> NotifyStructureComponentVisualsEdited;

        private void OnStructureComponentVisualsEdited()
        {
            if (NotifyStructureComponentVisualsEdited != null && !PausePropertyUpdateNotification)
            {
                if(CurrentSelectedNtVisuals.Count > 0 && CurrentSelectedBpVisuals.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(CurrentSelectedNtVisuals, CurrentSelectedBpVisuals));
                else if (CurrentSelectedNtVisuals.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(CurrentSelectedNtVisuals));
                else if (CurrentSelectedBpVisuals.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(CurrentSelectedBpVisuals));
            }  
        }

        private void OnStructureComponentVisualsEdited(List<NucleotideStructureElementVisualComponent> ntModified,
            List<BasePairStructureElementVisualComponent> bpModified)
        {
            if (NotifyStructureComponentVisualsEdited != null && !PausePropertyUpdateNotification)
            {
                if (ntModified.Count > 0 && bpModified.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(ntModified, bpModified));
                else if(ntModified.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(ntModified));
                else if(bpModified.Count > 0)
                    NotifyStructureComponentVisualsEdited(this, new MixedRangeEditSetEventArgs(bpModified));
            }
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty NucleotideFontFamilyProperty = DependencyProperty.Register("NucleotideFontFamily",
            typeof(FontFamily), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontWeightProperty = DependencyProperty.Register("NucleotideFontWeight",
           typeof(FontWeight?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontStyleProperty = DependencyProperty.Register("NucleotideFontStyle",
            typeof(FontStyle?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontSizeProperty = DependencyProperty.Register("NucleotideFontSize",
            typeof(double?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideRenderingStyleProperty = DependencyProperty.Register("NucleotideRenderingStyle",
            typeof(NucleotideRenderingMode?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFillProperty = DependencyProperty.Register("NucleotideFill",
            typeof(Brush), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty IsNucleotideSubstitutedProperty = DependencyProperty.Register("IsNucleotideSubstituted",
           typeof(bool?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideSubstitutionProperty = DependencyProperty.Register("NucleotideSubstitution",
            typeof(char?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleDiameterProperty = DependencyProperty.Register("NucleotideCircleDiameter",
            typeof(double?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleEdgeThicknessProperty = DependencyProperty.Register("NucleotideCircleEdgeThickness",
            typeof(double?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleIsFilledProperty = DependencyProperty.Register("NucleotideCircleIsFilled",
            typeof(bool?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleFillProperty = DependencyProperty.Register("NucleotideCircleFill",
            typeof(Brush), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleStrokeProperty = DependencyProperty.Register("NucleotideCircleStroke",
            typeof(Brush), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorRenderingStyleProperty = DependencyProperty.Register("BasePairConnectorRenderingStyle",
           typeof(BasePairConnectorRenderingMode?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorStrokeProperty = DependencyProperty.Register("BasePairConnectorStroke",
            typeof(Brush), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorStrokeThicknessProperty = DependencyProperty.Register("BasePairConnectorStrokeThickness",
            typeof(double?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorSizeProperty = DependencyProperty.Register("BasePairConnectorSize",
            typeof(double?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        /*public static readonly DependencyProperty BasePairConnectorOffsetProperty = DependencyProperty.Register("BasePairConnectorOffset",
            typeof(double?), typeof(HelixStructureElementVisualComponent), new FrameworkPropertyMetadata());*/

        public static readonly DependencyProperty BasePairConnectorFillProperty = DependencyProperty.Register("BasePairConnectorFill",
            typeof(Brush), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorIsVisibleProperty = DependencyProperty.Register("BasePairConnectorIsVisible",
            typeof(bool?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorIsFilledProperty = DependencyProperty.Register("BasePairConnectorIsFilled",
            typeof(bool?), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnComponentVisualPropertyChanged));

        public static readonly DependencyProperty LimitSelectionToBasePairsProperty = DependencyProperty.Register("LimitSelectionToBasePairs",
            typeof(bool), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(false, OnSelectionManagementPropertyChanged));

        public bool LimitSelectionToBasePairs
        {
            get { return (bool)GetValue(LimitSelectionToBasePairsProperty); }
            set { SetValue(LimitSelectionToBasePairsProperty, value); }
        }

        public static readonly DependencyProperty LimitSelectionToNucleotidesProperty = DependencyProperty.Register("LimitSelectionToNucleotides",
            typeof(bool), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(false, OnSelectionManagementPropertyChanged));

        public bool LimitSelectionToNucleotides
        {
            get { return (bool)GetValue(LimitSelectionToNucleotidesProperty); }
            set { SetValue(LimitSelectionToNucleotidesProperty, value); }
        }

        public static readonly DependencyProperty ProcessSelectionRangeAsNucleotidesProperty = DependencyProperty.Register("ProcessSelectionRangeAsNucleotides",
            typeof(bool), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(true, OnSelectionManagementPropertyChanged));

        public bool ProcessSelectionRangeAsNucleotides
        {
            get { return (bool)GetValue(ProcessSelectionRangeAsNucleotidesProperty); }
            set { SetValue(ProcessSelectionRangeAsNucleotidesProperty, value); }
        }

        private static readonly DependencyPropertyKey StartNucleotideIndexPropertyKey = DependencyProperty.RegisterReadOnly("StartNucleotideIndex",
            typeof(int), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty StartNucleotideIndexProperty = StartNucleotideIndexPropertyKey.DependencyProperty;

        public int StartNucleotideIndex
        {
            get { return (int)GetValue(StartNucleotideIndexProperty); }
            private set { SetValue(StartNucleotideIndexPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey EndNucleotideIndexPropertyKey = DependencyProperty.RegisterReadOnly("EndNucleotideIndex",
            typeof(int), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty EndNucleotideIndexProperty = EndNucleotideIndexPropertyKey.DependencyProperty;

        public int EndNucleotideIndex
        {
            get { return (int)GetValue(EndNucleotideIndexProperty); }
            private set { SetValue(EndNucleotideIndexPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey RangeSizePropertyKey = DependencyProperty.RegisterReadOnly("RangeSize",
            typeof(int), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty RangeSizeProperty = RangeSizePropertyKey.DependencyProperty;

        public int RangeSize
        {
            get { return (int)GetValue(RangeSizeProperty); }
            private set { SetValue(RangeSizePropertyKey, value); }
        }

        private static readonly DependencyPropertyKey ContextMessagePropertyKey = DependencyProperty.RegisterReadOnly("ContextMessage",
            typeof(string), typeof(RangeStructureElementVisualComponent), new FrameworkPropertyMetadata(string.Empty));
        public static readonly DependencyProperty ContextMessageProperty = ContextMessagePropertyKey.DependencyProperty;

        public string ContextMessage
        {
            get { return (string)GetValue(ContextMessageProperty); }
            private set { SetValue(ContextMessagePropertyKey, value); }
        }

        private static void OnSelectionManagementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RangeStructureElementVisualComponent rangeVisual = d as RangeStructureElementVisualComponent;
            if(rangeVisual != null)
            {
                if (!rangeVisual.IgnoreSelectionManagementPropertyNotifications && e.Property == ProcessSelectionRangeAsNucleotidesProperty)
                {
                    bool oldState = (bool)e.OldValue;
                    bool newState = (bool)e.NewValue;
                    if (oldState && !newState) //sel as nt only on and turned off
                    {
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                        rangeVisual.LimitSelectionToBasePairs = true;
                        rangeVisual.LimitSelectionToNucleotides = true;
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                        rangeVisual.SelectionManagementPropertyChangeUpdateSelection();
                        return;
                    }
                    else if (!oldState && newState) //sel as nt only off and turned on
                    {
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                        rangeVisual.LimitSelectionToBasePairs = false;
                        rangeVisual.LimitSelectionToNucleotides = false;
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                        rangeVisual.SelectionManagementPropertyChangeUpdateSelection();
                        return;
                    }
                }

                if (!rangeVisual.IgnoreSelectionManagementPropertyNotifications && e.Property == LimitSelectionToBasePairsProperty)
                {
                    bool oldState = (bool)e.OldValue;
                    bool newState = (bool)e.NewValue;
                    if (newState) //When turning on the base pair limit, all we care about is that sel as nt only is off
                    {
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                        rangeVisual.ProcessSelectionRangeAsNucleotides = false;
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                    }
                    else if (oldState && !newState) //When turning off the base pair limit, we have to check if the nt limit is off and if it is, toggle on the sel as nt only flag
                    {
                        if (!rangeVisual.LimitSelectionToNucleotides)
                        {
                            rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                            rangeVisual.ProcessSelectionRangeAsNucleotides = true;
                            rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                        }
                    }
                    rangeVisual.SelectionManagementPropertyChangeUpdateSelection();
                    return;
                }

                if (!rangeVisual.IgnoreSelectionManagementPropertyNotifications && e.Property == LimitSelectionToNucleotidesProperty)
                {
                    bool oldState = (bool)e.OldValue;
                    bool newState = (bool)e.NewValue;
                    if (newState) //When turning on the nt limit, we just have to make sure sel as nt only is off
                    {
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                        rangeVisual.ProcessSelectionRangeAsNucleotides = false;
                        rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                    }
                    else if (oldState && !newState) //When turning off the nt limit, we have to check if the base pair limit is off and if it is, toggle on the sel as nt only flag
                    {
                        if (!rangeVisual.LimitSelectionToBasePairs)
                        {
                            rangeVisual.IgnoreSelectionManagementPropertyNotifications = true; //Prevent propagation so this change can "appear" atomic
                            rangeVisual.ProcessSelectionRangeAsNucleotides = true;
                            rangeVisual.IgnoreSelectionManagementPropertyNotifications = false;
                        }
                    }
                    rangeVisual.SelectionManagementPropertyChangeUpdateSelection();
                    return;
                }
            }
        }

        private static void OnComponentVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RangeStructureElementVisualComponent rangeVisual = d as RangeStructureElementVisualComponent;
            if (rangeVisual != null)
            {
                if (e.Property == NucleotideFontFamilyProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideFontFamily = e.NewValue as FontFamily;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideFontFamily = e.NewValue as FontFamily;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideFontFamilyChange(e.NewValue as FontFamily);
                    if (rangeVisual.PausePropertyUpdateNotification) rangeVisual.PausePropertyUpdateNotification = false;
                    return;
                }

                if (e.Property == NucleotideRenderingStyleProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideRenderingStyle = e.NewValue as NucleotideRenderingMode?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideRenderingStyle = e.NewValue as NucleotideRenderingMode?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideRenderingStyleChange(e.NewValue as NucleotideRenderingMode?);
                    return;
                }

                if (e.Property == NucleotideFontStyleProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideFontStyle = e.NewValue as FontStyle?;
                        ntVisual.UninterruptEditNotification();   
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideFontStyle = e.NewValue as FontStyle?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideFontStyleChange(e.NewValue as FontStyle?);
                    return;
                }

                if (e.Property == NucleotideFontWeightProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideFontWeight = e.NewValue as FontWeight?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideFontWeight = e.NewValue as FontWeight?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideFontWeightChange(e.NewValue as FontWeight?);
                    return;
                }

                if (e.Property == NucleotideFontSizeProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideFontSize = e.NewValue as double?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideFontSize = e.NewValue as double?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideFontSizeChange(e.NewValue as double?);
                    return;
                }

                if (e.Property == NucleotideFillProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideFill = e.NewValue as Brush;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideFill = e.NewValue as Brush;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideFillChange(e.NewValue as Brush);
                    return;
                }

                if (e.Property == NucleotideSubstitutionProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideSubstitution = e.NewValue as char?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideSubstitution = e.NewValue as char?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideSubstitutionChange(e.NewValue as char?);
                    return;
                }

                if (e.Property == IsNucleotideSubstitutedProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.IsNucleotideSubstituted = e.NewValue as bool?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.IsNucleotideSubstituted = e.NewValue as bool?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkIsNucleotideSubstitutedChange(e.NewValue as bool?);
                    return;
                }

                if (e.Property == NucleotideCircleDiameterProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideCircleDiameter = e.NewValue as double?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideCircleDiameter = e.NewValue as double?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideCircleDiameterChange(e.NewValue as double?);
                    return;
                }

                if (e.Property == NucleotideCircleEdgeThicknessProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideCircleEdgeThickness = e.NewValue as double?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideCircleEdgeThickness = e.NewValue as double?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideCircleEdgeThicknessChange(e.NewValue as double?);
                    return;
                }

                if (e.Property == NucleotideCircleIsFilledProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideCircleIsFilled = e.NewValue as bool?;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideCircleIsFilled = e.NewValue as bool?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideCircleIsFilledChange(e.NewValue as bool?);
                    return;
                }

                if (e.Property == NucleotideCircleFillProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideCircleFill = e.NewValue as Brush;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideCircleFill = e.NewValue as Brush;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideCircleFillChange(e.NewValue as Brush);
                    return;
                }

                if (e.Property == NucleotideCircleStrokeProperty)
                {
                    //We force propagate the change to every element explicitly since this range construct is not persisted, but its a convenience mechanism for multiselect.
                    foreach (var ntVisual in rangeVisual.CurrentSelectedNtVisuals)
                    {
                        ntVisual.InterruptEditNotification();
                        ntVisual.NucleotideCircleStroke = e.NewValue as Brush;
                        ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.NucleotideCircleStroke = e.NewValue as Brush;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkNucleotideCircleStrokeChange(e.NewValue as Brush);
                    return;
                }

                if (e.Property == BasePairConnectorFillProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorFill = e.NewValue as Brush;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorFillChange(e.NewValue as Brush);
                    return;
                }

                if (e.Property == BasePairConnectorIsFilledProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorIsFilled = e.NewValue as bool?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorIsFilledChange(e.NewValue as bool?);
                    return;
                }

                if (e.Property == BasePairConnectorIsVisibleProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorIsVisible = e.NewValue as bool?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorIsVisibleChange(e.NewValue as bool?);
                    return;
                }

                if (e.Property == BasePairConnectorRenderingStyleProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorRenderingStyle = e.NewValue as BasePairConnectorRenderingMode?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorRenderingStyleChange(e.NewValue as BasePairConnectorRenderingMode?);
                    return;
                }

                if (e.Property == BasePairConnectorSizeProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorSize = e.NewValue as double?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorSizeChange(e.NewValue as double?);
                    return;
                }

                if (e.Property == BasePairConnectorStrokeProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorStroke = e.NewValue as Brush;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorStrokeChange(e.NewValue as Brush);
                    return;
                }

                if (e.Property == BasePairConnectorStrokeThicknessProperty)
                {
                    foreach (var bpVisual in rangeVisual.CurrentSelectedBpVisuals)
                    {
                        bpVisual.InterruptEditNotification();
                        bpVisual.BasePairConnectorStrokeThickness = e.NewValue as double?;
                        bpVisual.UninterruptEditNotification();
                    }

                    rangeVisual.OnStructureComponentVisualsEdited();
                    rangeVisual.ChangeSet.MarkBasePairConnectorStrokeThicknessChange(e.NewValue as double?);
                    return;
                }
            }
        }

        #endregion

        #region Properties

        public IEnumerable<NucleotideStructureElementVisualComponent> AllNucleotideVisuals
        {
            get { return AllSequenceVisuals.Values; }
        }

        public IEnumerable<NucleotideStructureElementVisualComponent> SelectedNucleotideVisuals
        {
            get
            {
                if (CurrentSelectedNtVisuals.Count > 0)
                {
                    return CurrentSelectedNtVisuals.AsEnumerable<NucleotideStructureElementVisualComponent>();
                }
                else { return Enumerable.Empty<NucleotideStructureElementVisualComponent>(); }
            }
        }

        public IEnumerable<BasePairStructureElementVisualComponent> SelectedBasePairVisuals
        {
            get
            {
                if (CurrentSelectedBpVisuals.Count > 0)
                {
                    return CurrentSelectedBpVisuals.AsEnumerable<BasePairStructureElementVisualComponent>();
                }
                else { return Enumerable.Empty<BasePairStructureElementVisualComponent>(); }
            }
        }

        #endregion

        #region IStructureElementVisualComponent Members

        public IEnumerable<int> NucleotideIndices
        {
            get 
            {
                if (FirstSelection == null || SecondSelection == null)
                    return Enumerable.Empty<int>();
                else
                {
                    if (StartNucleotideIndex > EndNucleotideIndex)
                        return Enumerable.Range(EndNucleotideIndex, ((StartNucleotideIndex - EndNucleotideIndex) + 1));
                    else
                        return Enumerable.Range(StartNucleotideIndex, ((EndNucleotideIndex - StartNucleotideIndex) + 1));
                }
            }
        }

        public Visual MakeVisualSnapshot()
        {
            //Since this visual copy is basically for printing and screen captures, we return null
            //becuase a loop has no rendering components except when it is selected. In contrast
            //a base pair component has a connector and a nucleotide component has the nucleotide representation (text, circle, rectangle).
            return null;
        }

        public Visual VisualComponent
        {
            get { return new DrawingVisual(); }
        }

        public bool IsComponentVisible
        {
            get { return true; }
            set { }
        }

        public IStructureElementVisualComponent ParentComponent
        {
            get;
            set;
        }

        public IEnumerable<IStructureElementVisualComponent> Children
        {
            get { return Enumerable.Empty<IStructureElementVisualComponent>(); }
        }

        public FontFamily NucleotideFontFamily
        {
            get { return GetValue(NucleotideFontFamilyProperty) as FontFamily; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideFontFamily = value as FontFamily;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideFontFamily = value as FontFamily;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideFontFamilyChange(value as FontFamily);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontFamilyProperty, value); 
            }
        }

        public FontWeight? NucleotideFontWeight
        {
            get { return GetValue(NucleotideFontWeightProperty) as FontWeight?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideFontWeight = value as FontWeight?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideFontWeight = value as FontWeight?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideFontWeightChange(value as FontWeight?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontWeightProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public FontStyle? NucleotideFontStyle
        {
            get { return GetValue(NucleotideFontStyleProperty) as FontStyle?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideFontStyle = value as FontStyle?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideFontStyle = value as FontStyle?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideFontStyleChange(value as FontStyle?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontStyleProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public double? NucleotideFontSize
        {
            get { return GetValue(NucleotideFontSizeProperty) as double?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideFontSize = value as double?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideFontSize = value as double?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideFontSizeChange(value as double?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontSizeProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public NucleotideRenderingMode? NucleotideRenderingStyle
        {
            get { return GetValue(NucleotideRenderingStyleProperty) as NucleotideRenderingMode?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideRenderingStyle = value as NucleotideRenderingMode?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideRenderingStyle = value as NucleotideRenderingMode?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideRenderingStyleChange(value as NucleotideRenderingMode?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideRenderingStyleProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public Brush NucleotideFill
        {
            get { return GetValue(NucleotideFillProperty) as Brush; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideFill = value as Brush;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideFill = value as Brush;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideFillChange(value as Brush);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFillProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public char? NucleotideSubstitution
        {
            get { return GetValue(NucleotideSubstitutionProperty) as char?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideSubstitution = value as char?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideSubstitution = value as char?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideSubstitutionChange(value as char?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideSubstitutionProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public bool? IsNucleotideSubstituted
        {
            get { return GetValue(IsNucleotideSubstitutedProperty) as bool?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.IsNucleotideSubstituted = value as bool?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.IsNucleotideSubstituted = value as bool?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkIsNucleotideSubstitutedChange(value as bool?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(IsNucleotideSubstitutedProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public double? NucleotideCircleDiameter
        {
            get { return GetValue(NucleotideCircleDiameterProperty) as double?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideCircleDiameter = value as double?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideCircleDiameter = value as double?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideCircleDiameterChange(value as double?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleDiameterProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public double? NucleotideCircleEdgeThickness
        {
            get { return GetValue(NucleotideCircleEdgeThicknessProperty) as double?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideCircleEdgeThickness = value as double?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideCircleEdgeThickness = value as double?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideCircleEdgeThicknessChange(value as double?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleEdgeThicknessProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public bool? NucleotideCircleIsFilled
        {
            get { return GetValue(NucleotideCircleIsFilledProperty) as bool?; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideCircleIsFilled = value as bool?;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideCircleIsFilled = value as bool?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideCircleIsFilledChange(value as bool?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleIsFilledProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public Brush NucleotideCircleFill
        {
            get { return GetValue(NucleotideCircleFillProperty) as Brush; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideCircleFill = value as Brush;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideCircleFill = value as Brush;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideCircleFillChange(value as Brush);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleFillProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public Brush NucleotideCircleStroke
        {
            get { return GetValue(NucleotideCircleStrokeProperty) as Brush; }
            set 
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                {
                    ntVisual.InterruptEditNotification();
                    ntVisual.NucleotideCircleStroke = value as Brush;
                    ntVisual.UninterruptEditNotification();
                }

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.NucleotideCircleStroke = value as Brush;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkNucleotideCircleStrokeChange(value as Brush);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleStrokeProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public bool? BasePairConnectorIsFilled
        {
            get { return GetValue(BasePairConnectorIsFilledProperty) as bool?; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorIsFilled = value as bool?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorIsFilledChange(value as bool?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorIsFilledProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public bool? BasePairConnectorIsVisible
        {
            get { return GetValue(BasePairConnectorIsVisibleProperty) as bool?; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorIsVisible = value as bool?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorIsVisibleChange(value as bool?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorIsVisibleProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public Brush BasePairConnectorFill
        {
            get { return GetValue(BasePairConnectorFillProperty) as Brush; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorFill = value as Brush;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorFillChange(value as Brush);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorFillProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public Brush BasePairConnectorStroke
        {
            get { return GetValue(BasePairConnectorStrokeProperty) as Brush; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorStroke = value as Brush;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorStrokeChange(value as Brush);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorStrokeProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public double? BasePairConnectorStrokeThickness
        {
            get { return GetValue(BasePairConnectorStrokeThicknessProperty) as double?; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorStrokeThickness = value as double?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorStrokeThicknessChange(value as double?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorStrokeThicknessProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public double? BasePairConnectorSize
        {
            get { return GetValue(BasePairConnectorSizeProperty) as double?; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorSize = value as double?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorSizeChange(value as double?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorSizeProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public BasePairConnectorRenderingMode? BasePairConnectorRenderingStyle
        {
            get { return GetValue(BasePairConnectorRenderingStyleProperty) as BasePairConnectorRenderingMode?; }
            set 
            {
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                {
                    bpVisual.InterruptEditNotification();
                    bpVisual.BasePairConnectorRenderingStyle = value as BasePairConnectorRenderingMode?;
                    bpVisual.UninterruptEditNotification();
                }

                OnStructureComponentVisualsEdited();
                ChangeSet.MarkBasePairConnectorRenderingStyleChange(value as BasePairConnectorRenderingMode?);
                PausePropertyUpdateNotification = true; //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorRenderingStyleProperty, value);
                PausePropertyUpdateNotification = false;
            }
        }

        public void NotifyParentHierarchyChanged()
        {
        }

        public void PropagateVisualPropertyChanged()
        {
        }

        public void AddChild(IStructureElementVisualComponent component)
        {
        }

        public void RemoveChild(IStructureElementVisualComponent component)
        {
        }

        public bool HitTest(Point point, bool includeChildren)
        {
            return false;
        }

        public void MakeSelectable()
        {
        }

        public void MakeUnselectable()
        {
        }

        public void Select(bool showNucleotideConnectors)
        {
        }

        public void Deselect()
        {
        }

        public void ContextuallySelect()
        {
        }

        public void ContextuallyDeselect()
        {
        }

        public bool IsNucleotideFillInheriting()
        {
            throw new NotSupportedException();
        }

        public bool IsNucleotideCircleFillInheriting()
        {
            throw new NotSupportedException();
        }

        public bool IsNucleotideCircleStrokeInheriting()
        {
            throw new NotSupportedException();
        }

        public bool IsBasePairConnectorFillInheriting()
        {
            throw new NotSupportedException();
        }

        public bool IsBasePairConnectorStrokeInheriting()
        {
            throw new NotSupportedException();
        }

        public void StartEditTracking()
        {
            throw new NotImplementedException();
        }

        public void InterruptEditTracking()
        {
            throw new NotImplementedException();
        }

        public void UninterruptEditTracking()
        {
            throw new NotImplementedException();
        }

        public void ApplyPropertyEditSet(StructureElementPropertyChangeSet set, bool interruptEditTracking, List<IBioSymbol> filter)
        {
            if (filter != null && filter.Count() > 0)
            {
                if (FirstSelection != null && SecondSelection != null)
                {
                    var rangeNtVisuals = AllNucleotideVisuals.Where(ntVisual => ntVisual.NucleotideIndex >= FirstSelection.NucleotideIndex 
                        && ntVisual.NucleotideIndex <= SecondSelection.NucleotideIndex);
                    var filteredRangeNtVisuals = from nt in filter
                                                 from ntVisual in rangeNtVisuals
                                                 where nt.Value.Equals(ntVisual.Data.Value)
                                                 select ntVisual;
                    foreach (var filteredNtVisual in filteredRangeNtVisuals)
                        filteredNtVisual.ApplyPropertyEditSet(set, true);
                    OnStructureComponentVisualsEdited(filteredRangeNtVisuals.ToList(), new List<BasePairStructureElementVisualComponent>());
                    ClearSelection();
                }
                
            }
            else { ApplyPropertyEditSet(set, interruptEditTracking); }
        }

        public void ApplyPropertyEditSet(StructureElementPropertyChangeSet set, bool interruptEditTracking)
        {
            if (FirstSelection != null && SecondSelection != null)
            {
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                    ntVisual.ApplyPropertyEditSet(set, true);

                foreach (var bpVisual in CurrentSelectedBpVisuals)
                    bpVisual.ApplyPropertyEditSet(set, true);
                OnStructureComponentVisualsEdited(CurrentSelectedNtVisuals, CurrentSelectedBpVisuals);
                ClearSelection();
            }
        }

        public void UndoAllPropertyChanges()
        {
            throw new NotImplementedException();
        }

        public void UndoLastPropertyChange()
        {
            throw new NotImplementedException();
        }

        public object SaveProperties()
        {
            throw new NotImplementedException();
        }

        public bool IsDirty()
        {
            throw new NotImplementedException();
        }

        #endregion

        public void SetFirstSelection(int ntIndex)
        {
            if (!AllSequenceVisuals.ContainsKey(ntIndex))
                throw new ArgumentException(string.Format("Passed an invalid nt index to anchor range"));

            SetFirstSelection(AllSequenceVisuals[ntIndex]);
        }

        public void SetSecondSelection(int ntIndex)
        {
            if (!AllSequenceVisuals.ContainsKey(ntIndex))
                throw new ArgumentException(string.Format("Passed an invalid nt index to end range"));

            SetSecondSelection(AllSequenceVisuals[ntIndex]);
        }

        public void SetFirstSelection(NucleotideStructureElementVisualComponent ntVisual)
        {
            if (ntVisual == null)
                throw new ArgumentException(string.Format("Passed an invalid visual to anchor range"));

            SecondSelection = null;
            EndNucleotideIndex = -1;
            RangeSize = 0;
            CurrentSelectedNtVisuals.Clear();
            CurrentSelectedNtVisualsWithChangesApplied.Clear();
            CurrentSelectedBpVisuals.Clear();
            CurrentSelectedBpVisualsWithChangesApplied.Clear();
            ChangeSet = new StructureElementPropertyChangeSet(true);

            FirstSelection = ntVisual;
            StartNucleotideIndex = ntVisual.NucleotideIndex;
            FirstSelection.Select(false);
            ContextMessage = BuildContextMessage();
        }

        public void SetSecondSelection(NucleotideStructureElementVisualComponent ntVisual)
        {
            if(ntVisual == null)
                throw new ArgumentException(string.Format("Passed an invalid visual to end range"));

            if (FirstSelection != null)
            {
                SecondSelection = ntVisual;
                EndNucleotideIndex = ntVisual.NucleotideIndex;
                SecondSelection.Select(false);
                RangeSize = Math.Abs(EndNucleotideIndex - StartNucleotideIndex) + 1;
                ProcessSelection();
                ContextMessage = BuildContextMessage();
            }
        }

        public void ClearSelection()
        {
            if (FirstSelection != null)
                FirstSelection.Deselect();

            if (SecondSelection != null)
                SecondSelection.Deselect();

            if (CurrentSelectedNtVisuals.Count() > 0)
                foreach (var ntVisual in CurrentSelectedNtVisuals)
                    ntVisual.Deselect();

            if (CurrentSelectedBpVisuals.Count() > 0)
                foreach (var bpVisual in CurrentSelectedBpVisuals)
                    bpVisual.Deselect();

            FirstSelection = null;
            SecondSelection = null;
            StartNucleotideIndex = -1;
            EndNucleotideIndex = -1;
            RangeSize = 0;
            CurrentSelectedNtVisuals.Clear();
            CurrentSelectedNtVisualsWithChangesApplied.Clear();
            CurrentSelectedBpVisuals.Clear();
            CurrentSelectedBpVisualsWithChangesApplied.Clear();
            ChangeSet = new StructureElementPropertyChangeSet(true);
            ProcessSelectionRangeAsNucleotides = true;
            PausePropertyUpdateNotification = false;

            //This means blowing away any state in the dependency properties (since we re-use this "visual"), obviously, this is somewhat of a blunt/hammer approach, but it gets the job done.
            SetValue(NucleotideFontFamilyProperty, null);
            SetValue(NucleotideFontWeightProperty, null);
            SetValue(NucleotideFontStyleProperty, null);
            SetValue(NucleotideFontSizeProperty, null);
            SetValue(NucleotideRenderingStyleProperty, null);
            SetValue(NucleotideFillProperty, null);
            SetValue(NucleotideSubstitutionProperty, null);
            SetValue(IsNucleotideSubstitutedProperty, null);
            SetValue(NucleotideCircleDiameterProperty, null);
            SetValue(NucleotideCircleEdgeThicknessProperty, null);
            SetValue(NucleotideCircleIsFilledProperty, null);
            SetValue(NucleotideCircleStrokeProperty, null);
            SetValue(BasePairConnectorFillProperty, null);
            SetValue(BasePairConnectorIsFilledProperty, null);
            SetValue(BasePairConnectorIsVisibleProperty, null);
            SetValue(BasePairConnectorRenderingStyleProperty, null);
            SetValue(BasePairConnectorSizeProperty, null);
            SetValue(BasePairConnectorStrokeProperty, null);
            SetValue(BasePairConnectorStrokeThicknessProperty, null);
            ContextMessage = BuildContextMessage();
        }

        public RangeStructureElementVisualComponent(Dictionary<int, NucleotideStructureElementVisualComponent> allSequenceVisuals,
            Dictionary<int, BasePairStructureElementVisualComponent> allBasePairVisuals)
        {
            Debug.Assert(allSequenceVisuals != null);
            Debug.Assert(allBasePairVisuals != null);
            AllSequenceVisuals = allSequenceVisuals;
            AllBasePairVisuals = allBasePairVisuals;
            StartNucleotideIndex = -1;
            EndNucleotideIndex = -1;
            RangeSize = 0;
            CurrentSelectedNtVisuals = new List<NucleotideStructureElementVisualComponent>();
            CurrentSelectedBpVisuals = new List<BasePairStructureElementVisualComponent>();
            CurrentSelectedNtVisualsWithChangesApplied = new List<NucleotideStructureElementVisualComponent>();
            CurrentSelectedBpVisualsWithChangesApplied = new List<BasePairStructureElementVisualComponent>();
            ContextMessage = BuildContextMessage();
        }

        public RangeStructureElementVisualComponent(Dictionary<int, NucleotideStructureElementVisualComponent> allSequenceVisuals)
        {
            Debug.Assert(allSequenceVisuals != null);
            AllSequenceVisuals = allSequenceVisuals;
            StartNucleotideIndex = -1;
            EndNucleotideIndex = -1;
            RangeSize = 0;
            CurrentSelectedNtVisuals = new List<NucleotideStructureElementVisualComponent>();
            CurrentSelectedNtVisualsWithChangesApplied = new List<NucleotideStructureElementVisualComponent>();
            CurrentSelectedBpVisuals = new List<BasePairStructureElementVisualComponent>();
            CurrentSelectedBpVisualsWithChangesApplied = new List<BasePairStructureElementVisualComponent>();
            AllBasePairVisuals = new Dictionary<int, BasePairStructureElementVisualComponent>();
            ChangeSet = new StructureElementPropertyChangeSet(true);
            IgnoreSelectionManagementPropertyNotifications = false;
            PausePropertyUpdateNotification = false;
            ContextMessage = BuildContextMessage();
        }

        private Dictionary<int, NucleotideStructureElementVisualComponent> AllSequenceVisuals { get; set; }
        private Dictionary<int, BasePairStructureElementVisualComponent> AllBasePairVisuals { get; set; }
        private List<NucleotideStructureElementVisualComponent> CurrentSelectedNtVisuals { get; set; }
        private List<NucleotideStructureElementVisualComponent> CurrentSelectedNtVisualsWithChangesApplied { get; set; }
        private List<BasePairStructureElementVisualComponent> CurrentSelectedBpVisuals { get; set; } //These would be the basepairs within the range.
        private List<BasePairStructureElementVisualComponent> CurrentSelectedBpVisualsWithChangesApplied { get; set; }
        private NucleotideStructureElementVisualComponent FirstSelection { get; set; }
        private NucleotideStructureElementVisualComponent SecondSelection { get; set; }
        private StructureElementPropertyChangeSet ChangeSet { get; set; }
        private bool IgnoreSelectionManagementPropertyNotifications { get; set; }
        private bool PausePropertyUpdateNotification { get; set; }

        private IEnumerable<int> _pairedNtIndexesEnumeration = null;
        private IEnumerable<int> PairedNtIndexes 
        {
            get
            {
                if (_pairedNtIndexesEnumeration == null)
                {
                    if (AllBasePairVisuals.Count() == 0)
                        _pairedNtIndexesEnumeration = Enumerable.Empty<int>();
                    else
                    {
                        _pairedNtIndexesEnumeration = from ntIdx in AllSequenceVisuals.Keys
                                                      from bp in AllBasePairVisuals.Values
                                                      where bp.Data.FivePrimeIndex == ntIdx || bp.Data.ThreePrimeIndex == ntIdx
                                                      select ntIdx;
                    }
                }
                return _pairedNtIndexesEnumeration;
            }
        }

        private void SelectionManagementPropertyChangeUpdateSelection()
        {
            if (FirstSelection != null && SecondSelection != null)
            {
                if (CurrentSelectedBpVisuals.Count() > 0)
                {
                    foreach (var selBP in CurrentSelectedBpVisuals)
                        selBP.Deselect();
                }

                if (CurrentSelectedNtVisuals.Count() > 0)
                {
                    foreach (var selNT in CurrentSelectedNtVisuals)
                        selNT.Deselect();
                }

                if (CurrentSelectedBpVisuals.Count > 0 && ChangeSet.ChangesRecorded > 0)
                {
                    CurrentSelectedBpVisualsWithChangesApplied.Clear();
                    CurrentSelectedBpVisualsWithChangesApplied.AddRange(CurrentSelectedBpVisuals);
                }
                CurrentSelectedBpVisuals.Clear();

                if (CurrentSelectedNtVisuals.Count > 0 && ChangeSet.ChangesRecorded > 0)
                {
                    CurrentSelectedNtVisualsWithChangesApplied.Clear();
                    CurrentSelectedNtVisualsWithChangesApplied.AddRange(CurrentSelectedNtVisuals);
                }
                CurrentSelectedNtVisuals.Clear();
                
                FirstSelection.Select(false);
                SecondSelection.Select(false);
                ProcessSelection();
                ContextMessage = BuildContextMessage();
            }
        }

        //Call this method to pickup any properties set on the range (allows you to force consecutive ranges to similar property sets.
        private void SynchronizeSelectionProperties()
        {
            if (ChangeSet.ChangesRecorded > 0)
            {
                if (CurrentSelectedNtVisualsWithChangesApplied.Count <= 0 && CurrentSelectedBpVisualsWithChangesApplied.Count <= 0)
                {
                    foreach (var ntVisual in CurrentSelectedNtVisuals)
                    {
                        //ntVisual.InterruptEditNotification();
                        ntVisual.ApplyPropertyEditSet(ChangeSet, true);
                        //ntVisual.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in CurrentSelectedBpVisuals)
                    {
                        //bpVisual.InterruptEditNotification();
                        bpVisual.ApplyPropertyEditSet(ChangeSet, true);
                        //bpVisual.UninterruptEditNotification();
                    }
                    OnStructureComponentVisualsEdited();
                }
                else if (CurrentSelectedNtVisualsWithChangesApplied.Count > 0 && CurrentSelectedBpVisualsWithChangesApplied.Count <= 0)
                {
                    var incrementalNtUpdates = CurrentSelectedNtVisuals.Except(CurrentSelectedNtVisualsWithChangesApplied);
                    foreach (var incrementalNtUpdate in incrementalNtUpdates)
                    {
                        //incrementalNtUpdate.InterruptEditNotification();
                        incrementalNtUpdate.ApplyPropertyEditSet(ChangeSet, true);
                        //incrementalNtUpdate.UninterruptEditNotification();
                    }

                    foreach (var bpVisual in CurrentSelectedBpVisuals)
                    {
                        //bpVisual.InterruptEditNotification();
                        bpVisual.ApplyPropertyEditSet(ChangeSet, true);
                        //bpVisual.UninterruptEditNotification();
                    }
                    OnStructureComponentVisualsEdited(incrementalNtUpdates.ToList<NucleotideStructureElementVisualComponent>(), CurrentSelectedBpVisuals);
                }
                else if (CurrentSelectedNtVisualsWithChangesApplied.Count <= 0 && CurrentSelectedBpVisualsWithChangesApplied.Count > 0)
                {
                    foreach (var ntVisual in CurrentSelectedNtVisuals)
                    {
                        //ntVisual.InterruptEditNotification();
                        ntVisual.ApplyPropertyEditSet(ChangeSet, true);
                        //ntVisual.UninterruptEditNotification();
                    }

                    var incrementalBpUpdates = CurrentSelectedBpVisuals.Except(CurrentSelectedBpVisualsWithChangesApplied);
                    foreach (var incrementalBpUpdate in incrementalBpUpdates)
                    {
                        //incrementalBpUpdate.InterruptEditNotification();
                        incrementalBpUpdate.ApplyPropertyEditSet(ChangeSet, true);
                        //incrementalBpUpdate.UninterruptEditNotification();
                    }
                    OnStructureComponentVisualsEdited(CurrentSelectedNtVisuals, incrementalBpUpdates.ToList<BasePairStructureElementVisualComponent>());
                }
                else
                {
                    var incrementalNtUpdates = CurrentSelectedNtVisuals.Except(CurrentSelectedNtVisualsWithChangesApplied);
                    foreach (var incrementalNtUpdate in incrementalNtUpdates)
                    {
                        //incrementalNtUpdate.InterruptEditNotification();
                        incrementalNtUpdate.ApplyPropertyEditSet(ChangeSet, true);
                        //incrementalNtUpdate.UninterruptEditNotification();
                    }

                    var incrementalBpUpdates = CurrentSelectedBpVisuals.Except(CurrentSelectedBpVisualsWithChangesApplied);
                    foreach (var incrementalBpUpdate in incrementalBpUpdates)
                    {
                        //incrementalBpUpdate.InterruptEditNotification();
                        incrementalBpUpdate.ApplyPropertyEditSet(ChangeSet, true);
                        //incrementalBpUpdate.UninterruptEditNotification();
                    }
                    OnStructureComponentVisualsEdited(incrementalNtUpdates.ToList<NucleotideStructureElementVisualComponent>(), incrementalBpUpdates.ToList<BasePairStructureElementVisualComponent>());
                }
            }
        }

        private void ProcessSelection()
        {
            //We require the range to be defined.
            Debug.Assert(FirstSelection != null);
            Debug.Assert(SecondSelection != null);

            //We identify the selected nt, any base pairs with in that set and the paired indices.
            IEnumerable<int> allNtInRange = (EndNucleotideIndex > StartNucleotideIndex) ? Enumerable.Range(StartNucleotideIndex, RangeSize) :
                    Enumerable.Range(EndNucleotideIndex, RangeSize);

            IEnumerable<IBasePairBioEntity> allBpInRange = AllBasePairVisuals.Values.Where(bpVisual => (((allNtInRange.First() <= bpVisual.Data.FivePrimeIndex) && (bpVisual.Data.FivePrimeIndex <= allNtInRange.Last()))
               || ((allNtInRange.First() <= bpVisual.Data.ThreePrimeIndex) && (bpVisual.Data.ThreePrimeIndex <= allNtInRange.Last())))).Select(bp => bp.Data);

            IEnumerable<int> allPairedNt = allBpInRange.Select(bp => bp.FivePrimeIndex).Union(allBpInRange.Select(bp => bp.ThreePrimeIndex));
            IEnumerable<int> allUnPairedNt = allNtInRange.Except(allPairedNt);

            if(ProcessSelectionRangeAsNucleotides)
            {
                //If were in this branch, we don't care about the other settings or if we have any base pairs within the selection range, were just doing a nucleotide multiselect.
                foreach (var ntIdx in allNtInRange)
                {   
                    CurrentSelectedNtVisuals.Add(AllSequenceVisuals[ntIdx]);
                    AllSequenceVisuals[ntIdx].Select(false);
                }
            }
            else
            {
                //Now, we have to look at our selection limits, build a truth table.
                //1. LimitSelectionToBasePairs == true && LimitSelectionToNucleotides == false, then we only select the base pairs in the range.
                //2. LimitSelectionToNucleotides == true && LimitSelectionToBasePairs == false, then we only select the unpaired nt in the range.
                //3. LimitSelectionToBasePairs == true && LimitSelectionToNucleotides == true, we select the base pairs and unpaired nt in the range.
                //4. LimitSelectionToBasePairs = false && LimitSelectionToNucleotides == false, selects nothing within the range, this should be avoided.

                if(LimitSelectionToBasePairs && LimitSelectionToNucleotides)
                {
                    foreach(var bp in allBpInRange)
                    {
                        CurrentSelectedBpVisuals.Add(AllBasePairVisuals[bp.FivePrimeIndex]);
                        AllBasePairVisuals[bp.FivePrimeIndex].Select(false);
                    }

                    foreach(var ntIdx in allUnPairedNt)
                    {
                        CurrentSelectedNtVisuals.Add(AllSequenceVisuals[ntIdx]);
                        AllSequenceVisuals[ntIdx].Select(false);
                    }
                }
                else if (LimitSelectionToBasePairs && !LimitSelectionToNucleotides)
                {
                    //Deselect the start and end points if they are not paired
                    if (!allPairedNt.Contains(FirstSelection.NucleotideIndex))
                        FirstSelection.Deselect();

                    if (!allPairedNt.Contains(SecondSelection.NucleotideIndex))
                        SecondSelection.Deselect();

                    foreach (var bp in allBpInRange)
                    {
                        CurrentSelectedBpVisuals.Add(AllBasePairVisuals[bp.FivePrimeIndex]);
                        AllBasePairVisuals[bp.FivePrimeIndex].Select(false);
                    }
                }
                else if (!LimitSelectionToBasePairs && LimitSelectionToNucleotides)
                {
                    //Deselect the start and end points if they are not unpaired
                    if (!allUnPairedNt.Contains(FirstSelection.NucleotideIndex))
                        FirstSelection.Deselect();

                    if (!allUnPairedNt.Contains(SecondSelection.NucleotideIndex))
                        SecondSelection.Deselect();

                    foreach (var ntIdx in allUnPairedNt)
                    {
                        CurrentSelectedNtVisuals.Add(AllSequenceVisuals[ntIdx]);
                        AllSequenceVisuals[ntIdx].Select(false);
                    }
                }
                else
                {
                    //This case is avoided by managing flag settings.
                }
            }

            if (StartNucleotideIndex > EndNucleotideIndex)
            {
                int tempStartIdx = StartNucleotideIndex;
                StartNucleotideIndex = EndNucleotideIndex;
                EndNucleotideIndex = tempStartIdx;
            }
            SynchronizeSelectionProperties();
        }

        private string BuildContextMessage()
        {
            if (FirstSelection == null)
                return string.Format("Select the first Nucleotide for the Range");
            else
            {
                if (SecondSelection == null)
                {
                    return string.Format("Selected {0} as the first Nucleotide for the Range, now select a second Nucleotide", FirstSelection.NucleotideIndex + 1);
                }
                else
                {
                    if (SelectedNucleotideVisuals.Count() > 0 && SelectedBasePairVisuals.Count() > 0)
                    {
                        return string.Format("Range from {0} to {1}, selecting {2} Nucleotides and {3} Base Pairs", FirstSelection.NucleotideIndex + 1, SecondSelection.NucleotideIndex + 1, SelectedNucleotideVisuals.Count(),
                            SelectedBasePairVisuals.Count());
                    }
                    else if (SelectedNucleotideVisuals.Count() > 0 && SelectedBasePairVisuals.Count() <= 0)
                    {
                        return string.Format("Range from {0} to {1}, selecting {2} Nucleotides", FirstSelection.NucleotideIndex + 1, SecondSelection.NucleotideIndex + 1, SelectedNucleotideVisuals.Count());
                    }
                    else if (SelectedNucleotideVisuals.Count() <= 0 && SelectedBasePairVisuals.Count() > 0)
                    {
                        return string.Format("Range from {0} to {1}, selecting {2} Base Pairs", FirstSelection.NucleotideIndex + 1, SecondSelection.NucleotideIndex + 1, SelectedBasePairVisuals.Count());
                    }
                    else
                    {
                        return string.Format("Range from {0} to {1} with internal error", FirstSelection.NucleotideIndex + 1, SecondSelection.NucleotideIndex + 1);
                    }
                }
            }
        }
    }
}
